package files

import (
	"archive/zip"
	"bufio"
	"compress/gzip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"gitee.com/xuender/oils/logs"
	"gitee.com/xuender/reader/pb"
)

type ArchiveService struct{}

func NewArchiveService() *ArchiveService {
	return &ArchiveService{}
}

func (p *ArchiveService) writeZip(fi *pb.FileInfo, w io.Writer) {
	archive, err := zip.OpenReader(fi.Archive)
	if err != nil {
		panic(err)
	}
	defer archive.Close()

	name := strings.Trim(fi.Path[len(fi.Archive):], "/")
	for _, f := range archive.File {
		if f.Name == name {
			file, err := f.Open()
			if err != nil {
				panic(err)
			}
			defer file.Close()

			_, _ = bufio.NewReader(file).WriteTo(w)

			return
		}
	}
}

func (p *ArchiveService) writeGzip(fi *pb.FileInfo, w io.Writer) {
	fr, err := os.Open(fi.Archive)
	if err != nil {
		logs.Panic(err)
	}
	defer fr.Close()

	gr, err := gzip.NewReader(fr)
	if err != nil {
		logs.Panic(err)
	}
	defer gr.Close()

	_, _ = bufio.NewReader(gr).WriteTo(w)
}

func (p *ArchiveService) Write(fi *pb.FileInfo, w io.Writer) {
	switch fi.ArchiveType {
	case pb.ArchiveType_zip:
		p.writeZip(fi, w)
	case pb.ArchiveType_gzip:
		p.writeGzip(fi, w)
	case pb.ArchiveType_none:
		panic("类型不支持")
	}
}

func (p *ArchiveService) folderZip(fi *pb.FileInfo, pfile map[string]*pb.FileInfo) []*pb.FileInfo {
	file := fi.Path
	if fi.Archive != "" {
		file = fi.Archive
	}

	archive, err := zip.OpenReader(file)
	if err != nil {
		panic(err)
	}
	defer archive.Close()

	fi.IsDir = true

	infos := []*pb.FileInfo{}
	root := fi.Path[len(file):]

	for _, f := range archive.File {
		if IsChild(root, f.Name) {
			// nolint: gosec
			path := filepath.Join(file, f.Name)
			id := Hash(path)
			zfi := &pb.FileInfo{
				Id:          id,
				Path:        path,
				Name:        strings.Trim(f.Name[len(root):], "/"),
				ModTime:     f.Modified.Unix(),
				IsDir:       f.FileInfo().IsDir(),
				Size:        f.FileInfo().Size(),
				Archive:     file,
				ArchiveType: pb.ArchiveType_zip,
				Type:        pb.FileType_archive,
			}

			re, err := f.Open()
			if err == nil {
				defer re.Close()

				head := make([]byte, 261)
				if size, err := re.Read(head); err == nil {
					zfi.Type, _ = FileTypeByBytes(head[:size], f.Name)
				}
			}

			infos = append(infos, zfi)
			pfile[id] = zfi
		}
	}

	return infos
}

func (p *ArchiveService) folderGzip(fi *pb.FileInfo, pfile map[string]*pb.FileInfo) []*pb.FileInfo {
	fr, err := os.Open(fi.Archive)
	if err != nil {
		logs.Panic(err)
	}
	defer fr.Close()

	gr, err := gzip.NewReader(fr)
	if err != nil {
		logs.Panic(err)
	}
	defer gr.Close()

	name := gr.Name
	if gr.Name == "" {
		name = fi.Name[:len(fi.Name)-len(filepath.Ext(fi.Name))]
	}

	path := filepath.Join(fi.Path, name)
	id := Hash(path)

	mt := gr.ModTime.Unix()
	if mt == 0 {
		mt = fi.ModTime
	}

	c := &pb.FileInfo{
		Id:          id,
		Name:        name,
		Path:        path,
		ModTime:     mt,
		Size:        fi.Size, // 尺寸有问题
		Archive:     fi.Archive,
		ArchiveType: pb.ArchiveType_gzip,
		Type:        pb.FileType_archive,
	}

	head := make([]byte, 261)
	if size, err := gr.Read(head); err == nil {
		c.Type, _ = FileTypeByBytes(head[:size], name)
	}

	pfile[id] = c

	return []*pb.FileInfo{c}
}

func (p *ArchiveService) Folder(fi *pb.FileInfo, pfile map[string]*pb.FileInfo) []*pb.FileInfo {
	if fi.Archive == "" {
		logs.Debug(fi)
		panic(fi.Name + "不是目录")
	}

	switch fi.ArchiveType {
	case pb.ArchiveType_zip:
		return p.folderZip(fi, pfile)
	case pb.ArchiveType_gzip:
		return p.folderGzip(fi, pfile)
	case pb.ArchiveType_none:
	}

	panic("类型不支持")
}

func (p *ArchiveService) readerZip(info *pb.FileInfo) (io.Reader, func()) {
	archive, err := zip.OpenReader(info.Archive)
	if err != nil {
		panic(err)
	}

	name := strings.Trim(info.Path[len(info.Archive):], "/")

	for _, f := range archive.File {
		if name == f.Name {
			r, err := f.Open()
			if err != nil {
				panic(err)
			}

			return r, func() {
				r.Close()
				archive.Close()
			}
		}
	}

	panic("文件未找到:" + info.Path)
}

func (p *ArchiveService) readerGzip(fi *pb.FileInfo) (io.Reader, func()) {
	fr, err := os.Open(fi.Archive)
	if err != nil {
		logs.Panic(err)
	}

	gr, err := gzip.NewReader(fr)
	if err != nil {
		logs.Panic(err)
	}

	return gr, func() {
		defer gr.Close()
		defer fr.Close()
	}
}

func (p *ArchiveService) Reader(fi *pb.FileInfo) (io.Reader, func()) {
	switch fi.ArchiveType {
	case pb.ArchiveType_zip:
		return p.readerZip(fi)
	case pb.ArchiveType_gzip:
		return p.readerGzip(fi)
	case pb.ArchiveType_none:
	}

	panic(fmt.Sprintf("类型不支持: %v", fi.Type))
}

// IsChild 是否是下级.
func IsChild(root, name string) bool {
	rs := split(root)
	ns := split(name)

	if len(ns)-len(rs) == 1 {
		for i, s := range rs {
			if ns[i] != s {
				return false
			}
		}

		return true
	}

	return false
}

func split(path string) []string {
	ret := []string{}

	for _, s := range strings.Split(path, "/") {
		if s != "" {
			ret = append(ret, s)
		}
	}

	return ret
}
